﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using System.Net.Sockets;
using EasyModbus;

namespace OPCService.Utils
{
    internal class ModbusTcpClient : IDisposable
    {
        private ModbusClient _client;
        private int _retryCount = 3;
        private int _retryDelayMs = 500;

        public event EventHandler<string> OnLog;
        public event EventHandler OnConnected;
        public event EventHandler OnDisconnected;

        public ModbusTcpClient(string ipAddress, int port)
        {
            _client = new ModbusClient(ipAddress, port);
        }

        #region 连接管理
        public bool Connect()
        {
            try
            {
                _client.Connect();
                OnLog?.Invoke(this, "连接成功");
                OnConnected?.Invoke(this, EventArgs.Empty);
                return true;
            }
            catch (Exception ex)
            {
                OnLog?.Invoke(this, $"连接失败: {ex.Message}");
                return false;
            }
        }

        public void Disconnect()
        {
            try
            {
                _client.Disconnect();
                OnLog?.Invoke(this, "断开连接");
                OnDisconnected?.Invoke(this, EventArgs.Empty);
            }
            catch (Exception ex)
            {
                OnLog?.Invoke(this, $"断开失败: {ex.Message}");
            }
        }

        public async Task<bool> ConnectAsync()
        {
            for (int i = 0; i < _retryCount; i++)
            {
                try
                {
                    await Task.Run(() => _client.Connect());
                    OnLog?.Invoke(this, "异步连接成功");
                    return true;
                }
                catch (Exception ex)
                {
                    OnLog?.Invoke(this, $"第 {i + 1} 次连接失败: {ex.Message}");
                    if (i < _retryCount - 1)
                        await Task.Delay(_retryDelayMs);
                }
            }
            return false;
        }
        #endregion

        #region 数据读写（同步）
        public int[] ReadHoldingRegisters(int startAddress, int count)
        {
            try
            {
                return _client.ReadHoldingRegisters(startAddress, count);
            }
            catch (Exception ex)
            {
                OnLog?.Invoke(this, $"读取寄存器失败: {ex.Message}");
                return null;
            }
        }

        public bool WriteSingleRegister(int address, int value)
        {
            try
            {
                _client.WriteSingleRegister(address, value);
                return true;
            }
            catch (Exception ex)
            {
                OnLog?.Invoke(this, $"写入单寄存器失败: {ex.Message}");
                return false;
            }
        }

        public bool WriteMultipleRegisters(int startAddress, int[] values)
        {
            try
            {
                _client.WriteMultipleRegisters(startAddress, values);
                return true;
            }
            catch (Exception ex)
            {
                OnLog?.Invoke(this, $"写入多寄存器失败: {ex.Message}");
                return false;
            }
        }

        public int[] ReadInputRegisters(int startAddress, int count)
        {
            try
            {
                return _client.ReadInputRegisters(startAddress, count);
            }
            catch (Exception ex)
            {
                OnLog?.Invoke(this, $"读取输入寄存器失败: {ex.Message}");
                return null;
            }
        }

        public bool[] ReadCoils(int startAddress, int count)
        {
            try
            {
                return _client.ReadCoils(startAddress, count);
            }
            catch (Exception ex)
            {
                OnLog?.Invoke(this, $"读取线圈失败: {ex.Message}");
                return null;
            }
        }

        public bool WriteSingleCoil(int address, bool value)
        {
            try
            {
                _client.WriteSingleCoil(address, value);
                return true;
            }
            catch (Exception ex)
            {
                OnLog?.Invoke(this, $"写入单线圈失败: {ex.Message}");
                return false;
            }
        }

        public bool WriteMultipleCoils(int startAddress, bool[] values)
        {
            try
            {
                _client.WriteMultipleCoils(startAddress, values);
                return true;
            }
            catch (Exception ex)
            {
                OnLog?.Invoke(this, $"写入多线圈失败: {ex.Message}");
                return false;
            }
        }
        #endregion

        #region 数据读写（异步）
        public async Task<int[]> ReadHoldingRegistersAsync(int startAddress, int count)
        {
            return await Task.Run(() => ReadHoldingRegisters(startAddress, count));
        }

        public async Task<bool> WriteSingleRegisterAsync(int address, int value)
        {
            return await Task.Run(() => WriteSingleRegister(address, value));
        }

        public async Task<bool> WriteMultipleRegistersAsync(int startAddress, int[] values)
        {
            return await Task.Run(() => WriteMultipleRegisters(startAddress, values));
        }

        public async Task<int[]> ReadInputRegistersAsync(int startAddress, int count)
        {
            return await Task.Run(() => ReadInputRegisters(startAddress, count));
        }

        public async Task<bool[]> ReadCoilsAsync(int startAddress, int count)
        {
            return await Task.Run(() => ReadCoils(startAddress, count));
        }

        public async Task<bool> WriteSingleCoilAsync(int address, bool value)
        {
            return await Task.Run(() => WriteSingleCoil(address, value));
        }

        public async Task<bool> WriteMultipleCoilsAsync(int startAddress, bool[] values)
        {
            return await Task.Run(() => WriteMultipleCoils(startAddress, values));
        }
        #endregion

        #region 数据类型转换
        public float ReadFloatFromRegisters(int startAddress)
        {
            var registers = ReadHoldingRegisters(startAddress, 2);
            if (registers == null || registers.Length < 2) return 0f;
            byte[] buffer = new byte[4];
            Buffer.BlockCopy(registers, 0, buffer, 0, 4);
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(buffer);  // 转换为小端格式
            }
            return BitConverter.ToSingle(buffer, 0);
        }

        public double ReadDoubleFromRegisters(int startAddress)
        {
            var registers = ReadHoldingRegisters(startAddress, 4);
            if (registers == null || registers.Length < 4) return 0d;
            byte[] buffer = new byte[8];
            Buffer.BlockCopy(registers, 0, buffer, 0, 8);
            return BitConverter.ToDouble(buffer, 0);
        }

        public string ReadStringFromRegisters(int startAddress, int length)
        {
            var registers = ReadHoldingRegisters(startAddress, length / 2);
            if (registers == null) return string.Empty;
            byte[] buffer = new byte[length];
            Buffer.BlockCopy(registers, 0, buffer, 0, length);
            return Encoding.ASCII.GetString(buffer);
        }

        public bool WriteFloatToRegisters(int startAddress, float value)
        {
            byte[] buffer = BitConverter.GetBytes(value);
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(buffer);  // 转换为大端格式
            }
            var registers = new int[] { BitConverter.ToInt16(buffer, 0), BitConverter.ToInt16(buffer, 2) };
            return WriteMultipleRegisters(startAddress, registers);
        }

        public bool WriteDoubleToRegisters(int startAddress, double value)
        {
            byte[] buffer = BitConverter.GetBytes(value);
            var registers = new int[]
            {
            BitConverter.ToInt16(buffer, 0),
            BitConverter.ToInt16(buffer, 2),
            BitConverter.ToInt16(buffer, 4),
            BitConverter.ToInt16(buffer, 6)
            };
            return WriteMultipleRegisters(startAddress, registers);
        }

        public bool WriteStringToRegisters(int startAddress, string value)
        {
            byte[] buffer = Encoding.ASCII.GetBytes(value.PadRight(2 * ((value.Length + 1) / 2), '\0'));
            int[] registers = new int[buffer.Length / 2];
            Buffer.BlockCopy(buffer, 0, registers, 0, buffer.Length);
            return WriteMultipleRegisters(startAddress, registers);
        }
        #endregion

        #region 高级设置
        public void SetRetryCount(int retryCount)
        {
            _retryCount = retryCount;
        }

        public void SetRetryDelay(int retryDelayMs)
        {
            _retryDelayMs = retryDelayMs;
        }
        #endregion

        #region IDisposable
        public void Dispose()
        {
            Disconnect();
        }
        #endregion
    }
}
